<?php
namespace Controller;

use Controller\RowdataBuilder;

/**
 * 处理导出类
 */
final class XmlExport
{
    private $id       = null;   //记录查询信息的记录id
    private $mgInfo   = null;   //查询记录
    private $savePath = null;   //保存路径
    private $db       = null;
    private $pageSize = 10000;  //第个文件记录数
    private $table    = '';     //查询异动的表名
    private $mgdb     = null;
    private $errorMsg = '';
    private $where    = '1';
    private $dataPackage = null;
    private $queryColumns = '*';

    function __construct($id, $dataPackage, $db = null)
    {
        if (empty($id)) {
            die('id false');
        }

        $this->db = is_null($db) ? new \MySql() : $db;
        $this->mgdb = new \mgdb();
        $this->id = $id;

        $this->_init();

        if (!isset($this->mgInfo['table'])) {
            die('table not false');
        }

        $this->table = $this->mgInfo['table'];

        if (empty($dataPackage[$this->table])) {
            die('data package not found');
        }

        $this->dataPackage = $dataPackage[$this->table];
        $this->queryColumns = join(',', $this->dataPackage['columns']);
    }

    private function _init()
    {
        $this->mgInfo = $this->getMgdbInfo();
    }

    /**
     * 获取数据
     * @param  [type] $i        [description]
     * @return [type]           [description]
     */
    function getData($i)
    {
        $db = new \Mysql();

        $sql =  "SELECT {$this->queryColumns} FROM {$this->table} WHERE {$this->where} limit " . ($i - 1) * $this->pageSize . ',' . $this->pageSize;

        $list = $db->getAll($sql);

        return $list;
    }

    /**
     * 获取查询记录信息
     * @return [type] [description]
     */
    public function getMgdbInfo()
    {
        //获取记录信息
        $info = $this->mgdb->where(array('_id' => new \MongoId($this->id)))->get('download');

        if (empty($info)) {
            return false;
        }

        $info = current($info);

        return $info;
    }

    /**
     * 记录总数
     * @param  integer $where [description]
     * @return [type]         [description]
     */
    function getTotal()
    {
        $db = new \MySql();
        $total = $db->count($this->table, $this->where);

        return $total;
    }

    /**
     * 创建并返回保存目录
     * @param  [type] $eid [description]
     * @return [type]      [description]
     */
    function getSavePath($eid)
    {
        //$dir = ROOT_PATH .'uploads';
        $savePath = UPLOADPATH . DS .  $eid.'_'.$this->id;

        if (!is_dir($savePath)) {
            if (false == mkdir($savePath, "755", true)){
                $this->errorMsg = '';
                return false;
            }
        }

        return $savePath;
    }

    /**
     * 执行导出
     * @param  [type] $pageSize [description]
     * @return [type]           [description]
     */
    public function run($pageSize = null)
    {
        if (empty($this->mgInfo)) {
            $this->errorMsg = '没有获取到发起下载记录';
            return false;
        }

        //$this->_createSearchBuilder();  //查询条件

        $this->pageSize = empty($pageSize) ? $this->pageSize : $pageSize; //每个文件多少条记录
        $total = $this->getTotal();    //总符合记录数

        $this->loopNum = ceil($total/$this->pageSize);

        $this->savePath = $this->getSavePath($this->mgInfo['eid']);  //保存路径

        if (empty($this->savePath)) {
            return false;
        }

        $this->tarPath = dirname($savePath);  //打包路径

        //导出
        $exportRes = $this->doExport();

        $tarFilename = $this->zipAcFile($this->mgInfo['eid']);

        //$url = $this->saveTotfs($tarFilename);

        //修改可下载状态
        //$updRes = $this->updDownloadStatus($url);

        //$res = $this->sendSms($this->mgInfo['phone']);

        return true;
    }

    /**
     * 导出操作
     * @return [type] [description]
     */
    public function doExport()
    {
        //declare (ticks = 1);
        //pcntl_signal(SIGCHLD, "garbage");

        $i = 0;
        $pids = [];

        while ($i < $this->loopNum) {
            $pid = pcntl_fork();

            if ($pid == -1) {
                break;
            }elseif($pid > 0){
                echo "parent continue pid:".getmypid().PHP_EOL;
                $pids[] = $pid;
                //pcntl_wait($status);
            }else{
                echo "child start pid:".getmypid().PHP_EOL;
                $list = $this->getData($i + 1);

                if (empty($list)) {
                    exit(0);
                }

                $this->saveTo($list, $i);
                exit(0);
            }

            $i++;
            sleep(1);
        }

        while(count($pids) > 0){
           $childId = pcntl_waitpid(-1, $status, WNOHANG);

           foreach ($pids as $k => $pid) {
               if ($childId == $pid) {
                  unset($pids[$k]);
               }
           }
        }

        echo 'parent loop end'.PHP_EOL;
        return true;
    }

    private function setHeader($writer)
    {
        $writer->writeSheetHeader('Sheet1',  $this->dataPackage['header']);
    }

    /**
     * 保存cvs文件
     * @param  [type] $list [数据]
     * @param  [type] $i    [第几个文件]
     * @return [type]       [description]
     */
    function saveTo($list, $i)
    {
        $writer = new \XLSXWriter();
        $this->setHeader($writer);

        $startNum = ($i * $this->pageSize) + 1;
        $endNum = $startNum + $this->pageSize - 1;

        $fileName =  date('Ymd').'_' . $startNum. '_' . $endNum.'.xls';

        if (file_exists($fileName)) {
            @unlink($this->savePath.DS.$fileName);
        }

        $fullFileName = $this->savePath . DS . $fileName;

        foreach ($list as $k => $v) {
            $bulider = XmlRowdataBuilder::getInstance();
            $rowData = $bulider->buildData($v, $this->table);

            $writer->writeSheetRow('Sheet1', $rowData);
        }

        unset($list, $v);
        $writer->writeToFile($fullFileName);

        return true;
    }

    /**
     * 创建目录
     * @param  [type] $filename [description]
     * @return [type]           [description]
     */
    function makedir($filename){
        return (mkdir($filename, 0755));
    }

    //打包文件
    function zipAcFile($eid)
    {
        $tarPath = dirname($this->savePath);

        $path = explode(DS, $this->savePath);
        $finialPath = array_pop($path);

        $datalist = $this->listDir($this->savePath);

        $fileName = $tarPath . DS . $eid . '_' . $this->id . '.zip';

        if(file_exists($fileName)){
            @unlink($fileName);
        }

        //重新生成文件
        $zip = new \ZipArchive();//使用本类，linux需开启zlib，windows需取消php_zip.dll前的注释

        if ($zip->open($fileName, \ZIPARCHIVE::CREATE)!==TRUE) {
            $this->errorMsg = '无法打开文件，或者文件创建失败';
            return false;
        }

        foreach($datalist as $val){
            if(file_exists($val)){
                $zip->addFile($val, basename($val));//第二个参数是放在压缩包中的文件名称，如果文件可能会有重复，就需要注意一下
            }
        }

        $zip->close();//关闭
        return $fileName;
    }

    /**
     * 遍历目录
     * @param  [type] $dir [description]
     * @return [type]      [description]
     */
    function listDir($dir){
        $result = array();
        if (is_dir($dir)){
            $file_dir = scandir($dir);
            foreach($file_dir as $file){
                if ($file == '.' || $file == '..'){
                    continue;
                }
                elseif (is_dir($dir.$file)){
                    $result = array_merge($result, list_dir($dir.$file.DS));
                }
                else{
                    array_push($result, $dir.DS.$file);
                }
            }
        }
        return $result;
    }

    /**
     * 上传到tfs
     * @param  [type] $fullFilename [打包好的压缩文件绝对路径]
     * @return [type]               [description]
     */
    function saveTotfs($fullFilename)
    {
        $fileObj['file'] = array(
            'tmp_name' => $fullFilename,
            'name' => basename($fullFilename),
        );

        $up = new \upload($fileObj,'');

        $res = $up->run('file');

        if (!$res) {
            exit($up->errmsg());
        }

        $url = $up->getInfo()[0]['saveName'];
        return $url;
    }

    /**
     * 向雇员发送短信
     * @param  [type] $phone [description]
     * @return [type]        [description]
     */
    private function sendSms($phone)
    {
        $sms = new \sms();
        $res = $sms->SendValidateSMS($phone);

        if (!$res) {
            $this->errorMsg = $sms->getError();
            //return ($sms->getError());
            return false;
        }

        return true;
    }

    /**
     * 修改查询信息，标记可下载
     * @param  [type] $url [description]
     * @return [type]      [description]
     */
    function updDownloadStatus($url)
    {
        $where = array(
            '_id' => new \MongoId($this->id),
        );

        $res = $this->mgdb->where($where)->set(array('status' => intval(1),'url' => $url))->update('download');

        return $res;
    }

    /**
     * 根据不同的表拼接查询条件
     */
    private function _createSearchBuilder()
    {
        $info = $this->mgInfo;

        switch($this->table){
        case 'pay_account_cash_out':
            $needsParams = array(
                'co_day_time',
                'co_day_time2',
                'co_state'
            );

            foreach ($needsParams as $v) {
                if (isset($info[$v]) && '' != $info[$v]) {
                    switch($v){
                    case 'co_day_time':
                        $this->where .= " AND co_day_time > '{$info[$v]}'";
                        break;
                    case 'co_day_time2':
                        $this->where .= " AND co_day_time < '{$info[$v]}'";
                        break;
                    case 'co_state':
                        $this->where .= " AND co_state = '{$info[$v]}'";
                        break;
                    }
                }
            }
            break;
        case 'pay_account_store_tran':
            break;
        case 'pay_account_score_tran':
        case 'pay_account_store_tran':
        case 'pay_account_tang_tran':
            $needsParams = array(
                'sc_day_time',
                'sc_day_time2',
                'phone',
            );

            foreach ($needsParams as $v) {
                if (isset($info[$v]) && !empty($info[$v])) {
                    switch($v){
                    case 'sc_day_time':
                        $this->where .= " AND sc_day_time > '{$info[$v]}'";
                        break;
                    case 'sc_day_time2':
                        $this->where .= " AND sc_day_time < '{$info[$v]}'";
                        break;
                    }
                }
            }
        default:
            break;
        }
    }

    public function getError()
    {
        return $this->errorMsg;
    }
}
